package org.apache.skywalking.apm.plugin.kepler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xiaomaoguai.fcp.pre.kepler.common.constants.KeplerCommonConstant;
import com.xiaomaoguai.fcp.pre.kepler.common.utils.JsonUtils;
import com.xiaomaoguai.fcp.pre.kepler.router.router.RouterPipeline;
import org.apache.skywalking.apm.agent.core.context.CarrierItem;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.tag.StringTag;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.kepler.utils.WrapperDiffUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author WeiHui-Z
 * @version v1.0.0
 * @date 2019/6/5 17:22
 * @since JDK 1.8
 */
public class KeplerStandardHeadHandlerInterceptor implements InstanceMethodsAroundInterceptor {

	@Override
	public void beforeMethod(EnhancedInstance enhancedInstance, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult methodInterceptResult) throws Throwable {
		@SuppressWarnings("rawtypes") final RouterPipeline routerPipeline = (RouterPipeline) allArguments[0];
		final Object innerParam = routerPipeline.getInnerParam();
		final String routerName = routerPipeline.getRouterName();

		String jsonString = JSON.toJSONString(innerParam);
		JSONObject jsonObject = JSON.parseObject(jsonString);
		final String productCode = jsonObject.getString(KeplerCommonConstant.PRODUCT_CODE);

		String operationName = "Begin=>" + routerName + ".RouterIN";

		AbstractSpan activeSpan;
		HttpServletRequest request = (HttpServletRequest) ContextManager.getRuntimeContext().get(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT);
		if (request != null) {
			ContextCarrier contextCarrier = new ContextCarrier();
			CarrierItem next = contextCarrier.items();
			while (next.hasNext()) {
				next = next.next();
				next.setHeadValue(request.getHeader(next.getHeadKey()));
			}
			activeSpan = ContextManager.createEntrySpan(operationName, contextCarrier);
		} else {
			activeSpan = ContextManager.createLocalSpan(operationName);
		}

		activeSpan.tag(new StringTag("routerName"), routerName);
		activeSpan.tag(new StringTag("productCode"), productCode);
		String formatJson = JsonUtils.toFormatJsonString(innerParam);
		activeSpan.tag(new StringTag("beforeRouterIn"), formatJson);
		ContextManager.getRuntimeContext().put(Constants.CONVERT_VALUE, formatJson);
	}

	@Override
	public Object afterMethod(EnhancedInstance enhancedInstance, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object o) throws Throwable {
		final RouterPipeline handlerContext = (RouterPipeline) allArguments[0];
		final Map<String, Object> context = handlerContext.getBuf().getContext();

		final String afterJson = JsonUtils.toFormatJsonString(context);
		final String beforeJson = (String) ContextManager.getRuntimeContext().get(Constants.CONVERT_VALUE);
		final String diff = WrapperDiffUtils.getDiff(afterJson, beforeJson);

		AbstractSpan activeSpan = ContextManager.activeSpan();
		activeSpan.tag(new StringTag("afterRouterIn"), afterJson);
		activeSpan.tag(new StringTag("diff"), diff);
		ContextManager.stopSpan();
		return o;
	}

	@Override
	public void handleMethodException(EnhancedInstance enhancedInstance, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable throwable) {
		ContextManager.activeSpan().errorOccurred().log(throwable);
	}

}
